/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.context.service;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;

import org.zhiqim.httpd.HttpExecutor;
import org.zhiqim.httpd.HttpRequest;
import org.zhiqim.httpd.HttpResponse;
import org.zhiqim.httpd.context.ZmlContextConstants;

import org.zhiqim.kernel.util.Validates;

/**
 * 验证码调用，业务系统可通过配置覆盖
 * /service/vcode.jpg
 * 
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public final class VcodeService implements HttpExecutor, ZmlContextConstants
{
    private static Random random = new Random();
    
    @Override
    public boolean isMatch(String pathInContext)
    {
        return _PATH_SERVICE_VCODE_.equals(pathInContext);
    }
    
    @Override
    public void handle(HttpRequest request,HttpResponse response) throws IOException
    {
        if (!Validates.isWindows())
            System.setProperty("java.awt.headless", "true");
        
        int width = request.getParameterInt("width", request.getContextAttributeInt("vcode.width", 60));
        int height = request.getParameterInt("height", request.getContextAttributeInt("vcode.height", 28));//有z-input指定的高度相等
        String textColor = request.getParameter("textColor", request.getContextAttributeString("vcode.textColor", "#ffffff"));
        String bgColor = request.getParameter("bgColor", request.getContextAttributeString("vcode.bgColor", "#0826A9"));//蓝色
        int fontSize = request.getParameterInt("fontSize", request.getContextAttributeInt("vcode.fontSize", 18));
        String fontFamily = request.getParameter("fontFamily", request.getContextAttributeString("vcode.fontFamily", "Arial"));
        String fontWidthRatio = request.getParameter("fontWidthRatio", request.getContextAttributeString("vcode.fontWidthRatio", "0.6"));//Arial宽度比定为0.5
        boolean isYawp = request.getParameterBoolean("yawp", request.getContextAttributeBoolean("vcode.yawp", false));
        boolean isDisturb = request.getParameterBoolean("disturb", request.getContextAttributeBoolean("vcode.disturb", false));
        
        //计算字间隔
        float wSplit = width/4 - fontSize*Float.parseFloat(fontWidthRatio);
        if (wSplit < 0)
            wSplit = 0;
        float hSplit = (height - fontSize) / 2;
        if (hSplit < 0)
            hSplit = 0;

        //字体颜色
        int textRed = Integer.parseInt(textColor.substring(1, 3), 16);
        int textGreen = Integer.parseInt(textColor.substring(3, 5), 16);
        int textBlue = Integer.parseInt(textColor.substring(5), 16);
        Color text = new Color(textRed, textGreen, textBlue);
        
        //背景颜色
        int bgRed = Integer.parseInt(bgColor.substring(1, 3), 16);
        int bgGreen = Integer.parseInt(bgColor.substring(3, 5), 16);
        int bgBlue = Integer.parseInt(bgColor.substring(5), 16);
        Color bg = new Color(bgRed, bgGreen, bgBlue);
        
        //字体
        Font font = new Font(fontFamily, Font.PLAIN, fontSize);

        //设置为图片信息
        response.setContentType("image/jpeg"); 
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        
        Graphics g = image.getGraphics();
        g.setColor(bg);
        g.fillRect(0, 0, width, height);
        g.setFont(font);
        
        if (isYawp)
        {
            doAddYawp(image, width, height);
        }
        
        if (isDisturb)
        {
            doAddDisturb(g, width, height);
        }
        
        Random random = new Random();
        String sRand = "";
        for (int i=0;i<4;i++)
        {
            String rand = String.valueOf(random.nextInt(10));
            sRand += rand;

            g.setColor(text);
            int x = Math.round((width/4) * i + wSplit / 2);
            int y = Math.round(fontSize + hSplit) - 2;
            g.drawString(rand, x, y);
        }
        g.dispose();
        
        request.getSession().setAttribute(ZC_VCODE_SESSION_NAME ,sRand);
        ImageIO.write(image, "JPEG", response.getOutputStream());
    }
    
    /** 增加噪点 */
    private void doAddYawp(BufferedImage image, int width, int height)
    {
        float yawpRate = 0.08f;// 噪声率
        int area = (int) (yawpRate * width * height);
        for (int i = 0; i < area; i++)
        {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int rgb = getRandomColorInt();
            image.setRGB(x, y, rgb);
        }
    }
    
    /** 增加干扰线 */
    private void doAddDisturb(Graphics g, int width, int height)
    {
        g.setColor(getRandomColor(160, 200));
        
        Random rd = new Random();
        for (int i = 0; i < 20; i++)
        {
            int x = rd.nextInt(width - 1);
            int y = rd.nextInt(height - 1);
            int xl = rd.nextInt(6) + 1;
            int yl = rd.nextInt(12) + 1;
            g.drawLine(x, y, x + xl + 40, y + yl + 20);
        }
    }
    
    private Color getRandomColor(int fc, int bc)
    {
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }
    
    private int getRandomColorInt()
    {
        int[] rgb = new int[3];
        for (int i = 0; i < 3; i++)
        {
            rgb[i] = random.nextInt(255);
        }
        
        int color = 0;
        for (int c : rgb)
        {
            color = color << 8;
            color = color | c;
        }
        return color;
    }
}
